package task;

import app.FileMeta;
import callback.FileScannerCallBack;
import com.sun.org.apache.xpath.internal.operations.Bool;
import lombok.Getter;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

//文件扫描任务的类
@Getter
public class FileScanner {
    private AtomicInteger fileNum = new AtomicInteger();
    //当前扫描的文件个数，为了保证多线程的安全，不能使用int，采用juc下的AtomicInteger原子类来对计数器进行原子操作
    private AtomicInteger dirNum = new AtomicInteger(1);
    //当前扫描的文件夹个数,初始化为1，表示从根目录下开始的扫描工作

    //子线程的个数，子线程个数为0，主线程才继续执行
    private AtomicInteger threadCount = new AtomicInteger();

    //获取当前运行主机的可用cpu个数
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    //使用线程池创建对象
    private static ThreadPoolExecutor pool = new ThreadPoolExecutor(CPU_COUNT,CPU_COUNT * 2
    ,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new ThreadPoolExecutor.AbortPolicy());

    //主线程中子线程个数为0时，主线程才继续执行,
    private CountDownLatch latch = new CountDownLatch(1);
    private FileScannerCallBack callBack;

    public FileScanner(FileScannerCallBack callBack){
        this.callBack = callBack;
    }
    /**
     * 根据传入的文件路径进行扫描任务的入口方法
     * 将扫描文件夹的工作交给子线程执行，当所有子线程执行完毕以后，统计文件夹和文件的个数，主线程关闭以后恢复主线程的执行
     * @param filePath 文件路径
     */
    public void scan(File filePath){
        //将选择的文件夹传入，打印目录
        System.out.println("当前选择的根目录为："+ filePath.getPath());
        long start = System.nanoTime();
        threadCount.incrementAndGet();//扫描当前目录时，就已经有一个工作线程了，所以就需要在扫描根目录时++
        scanInternal(filePath);
        try {
            latch.await();
        } catch (InterruptedException e) {
            System.err.println("扫描任务终端，根目录为：" + filePath.getPath());
            throw new RuntimeException(e);
        }finally {
            //如果文件是正常扫描结束以后，关闭线程池
            //如果扫描是因为中断导致的，则关闭线程池
            pool.shutdownNow();
        }
        long end = System.nanoTime();
        System.out.println("共扫描了文件个数为：" + fileNum.get()+ "个");
        System.out.println("共扫描了文件夹个数为：" + dirNum.get()+ "个");
        System.out.println("扫描共用时：" + (end - start) * 1.0 / 1000000 + "ms");
    }

    private void scanInternal(File filePath) {
        //遍历终止条件
        if (filePath == null){
            return;
        }
        pool.submit(() -> {
            this.callBack.callback(filePath);
            //拿到当前目录下的所有的内容
            File[] files = filePath.listFiles();
            //遍历file对象,根据是否时文件夹进行区别处理
            for (File file:files) {
                if (file.isDirectory()){
                    dirNum.incrementAndGet();//等于++i
                    threadCount.incrementAndGet();//如果是文件夹，线程数++，然后子线程去扫描子文件夹
                    scanInternal(file);
                }else {
                    fileNum.incrementAndGet();//等于++i
                }
            }
            //for循环结束，表示当前目录的的所有文件、文件夹（递归处理）全都扫描添加到了数据库，让当前线程的数量--
            threadCount.decrementAndGet();
            if (threadCount.get() == 0){
                latch.countDown();//线程数为0唤醒主线程
            }
        });
    }
}
